package com.gwl.concurrent.newcomponent;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.concurrent.TimeUnit.*; 
import static java.lang.System.out; 

class DelayedTask implements Runnable, Delayed {

	private static final AtomicInteger COUNTER = new AtomicInteger(0); 
	private final int id = COUNTER.incrementAndGet(); 
	private final int delta; 
	private final long trigger; 
	protected static List<DelayedTask> sequence = new ArrayList<DelayedTask>(); 
	
	public DelayedTask(int delayInMilliseconds) { 
		delta = delayInMilliseconds; 
		trigger = System.nanoTime() + NANOSECONDS.convert(delta, MILLISECONDS); 
		sequence.add(this); 
	}
	
	@Override
	public int compareTo(Delayed o) {
		DelayedTask that = (DelayedTask)o; 
		if(trigger < that.trigger) return -1; 
		if(trigger > that.trigger) return 1; 
		return 0; 
	}

	@Override
	public long getDelay(TimeUnit unit) { 
		return unit.convert(trigger - System.nanoTime(), NANOSECONDS); 
	}

	public String summary() { 
		return "(" + id + ": " + delta + ")"; 
	} 
	public String toString() { 
		return String.format("[%1$-4d]", delta) + 
			" Task " + id; 
	}
	@Override
	public void run() {
		out.print(this + " "); 
	} 

	public static class EndSentinel extends DelayedTask {
		private ExecutorService exec; 
		public EndSentinel(int delayInMilliseconds, ExecutorService e) {
			super(delayInMilliseconds);
			exec = e; 
		} 
		public void run() { 
			for(DelayedTask pt: sequence) { 
				out.println(pt.summary() + " "); 
			} 
			out.println(); 
			out.println(this + " Calling shutdownNow()"); 
			exec.shutdownNow(); 
		}
	} 
	
}

class DelayedTaskConsumer implements Runnable {
	private DelayQueue<DelayedTask> queue; 
	public DelayedTaskConsumer(DelayQueue<DelayedTask> q) { 
		queue = q; 
	}
	@Override
	public void run() {
		try {
			while(!Thread.interrupted())
				queue.take().run();
		} catch (InterruptedException e) {
			// Acceptable way to exit
		} 
		out.println("Finished DelayedTaskConsumer"); 
	} 
}

public class DelayQueueDemo {

	public static void main(String[] args) {
		Random rand = new Random(47); 
		ExecutorService exec = Executors.newCachedThreadPool(); 
		DelayQueue<DelayedTask> queue = new DelayQueue<DelayedTask>(); 
		// Fill with tasks have random delays: 
		for(int i = 0; i < 20; i++) 
			queue.put(new DelayedTask(rand.nextInt(5000))); 
		queue.add(new DelayedTask.EndSentinel(5000, exec)); 
		exec.execute(new DelayedTaskConsumer(queue)); 
	}

}
